package com.chat.common.user.service.impl;

import com.chat.common.common.algorithm.sensitiveWord.SensitiveWordBs;
import com.chat.common.common.domain.enums.YesOrNoEnum;
import com.chat.common.common.domain.vo.resp.ApiResult;
import com.chat.common.common.event.UserBlackEvent;
import com.chat.common.common.event.UserRegisterEvent;
import com.chat.common.common.service.cache.UserSummaryCache;
import com.chat.common.common.utils.AssertUtil;
import com.chat.common.user.dao.BlackDao;
import com.chat.common.user.dao.ItemConfigDao;
import com.chat.common.user.dao.UserBackpackDao;
import com.chat.common.user.dao.UserDao;
import com.chat.common.user.domain.dto.ItemInfoDTO;
import com.chat.common.user.domain.dto.SummeryInfoDTO;
import com.chat.common.user.domain.entity.*;
import com.chat.common.user.domain.enums.BlackTypeEnum;
import com.chat.common.user.domain.enums.ItemEnum;
import com.chat.common.user.domain.enums.ItemTypeEnum;
import com.chat.common.user.domain.vo.req.BadgeResp;
import com.chat.common.user.domain.vo.req.BlackReq;
import com.chat.common.user.domain.vo.req.user.ItemInfoReq;
import com.chat.common.user.domain.vo.req.user.SummeryInfoReq;
import com.chat.common.user.domain.vo.resp.UserInfoResp;
import com.chat.common.user.service.UserService;
import com.chat.common.user.service.adapter.UserAdapter;
import com.chat.common.user.service.cache.ItemCache;
import com.chat.common.user.service.cache.UserCache;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    @Resource
    private UserDao userDao;
    @Resource
    private UserBackpackDao userBackpackDao;
    @Resource
    private ItemCache itemCache;
    @Resource
    private ItemConfigDao itemConfigDao;

    @Resource
    private BlackDao blackDao;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private UserCache userCache;
    @Resource
    private UserSummaryCache userSummaryCache;
    @Resource
    private SensitiveWordBs sensitiveWordBs;

    @Override
    @Transactional
    public long register(User insert) {
        boolean save = userDao.save(insert);
        //用户注册事件
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this,insert));
        return insert.getId();
    }

    @Override
    public UserInfoResp getUserInfo(Long uid) {
        User user = userDao.getById(uid);
        //用户改名卡 次数
        Integer modifyNameCount = userBackpackDao.getModifyNameCount(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        return UserAdapter.buildUserInfo(user,modifyNameCount);
    }

    /**
     * 修改用户名称
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyName(Long uid, String name) {
        AssertUtil.isFalse(sensitiveWordBs.hasSensitiveWord(name), "名字中包含敏感词，请重新输入"); // 判断名字中有没有敏感词
        //因为我们限制了 用户的名称是唯一的 所以直接查询
        User user = userDao.getByName(name);
        //传递过来的名字 查询出来不为null 则抛异常
        AssertUtil.isEmpty(user,"名字重复了 换个名字试试");
        //获取改名卡数量
        UserBackpack firstValidItem = userBackpackDao.getFirstValidItem(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        AssertUtil.isNotEmpty(firstValidItem,"改名卡数量不足");
        //使用改名卡
        boolean success = userBackpackDao.useModifyName(firstValidItem.getId(), YesOrNoEnum.YES.getStatus());
        //修改名字
        if (success){
            userDao.modifyName(uid, name);
        }
    }

    //徽章列表
    @Override
    public List<BadgeResp> badges(Long uid) {
        //查询所有的徽章
        List<ItemConfig> itemConfigList = itemCache.getByType(ItemTypeEnum.BADGE.getType());
        //查询用户拥有的徽章
        List<UserBackpack> backpackList = userBackpackDao.getByItemIds(uid, YesOrNoEnum.NO.getStatus());
        //查询用户 佩戴的徽章
        User user = userDao.getById(uid);
        //将数据组装
        return UserAdapter.buildBadgeResp(itemConfigList,backpackList,user);
    }

    @Override
    public void wearingBadge(Long uid,Long itemId) {
        //查询用户是否拥有 徽章
        UserBackpack firstValidItem = userBackpackDao.getFirstValidItem(uid, itemId);
        AssertUtil.isNotEmpty(firstValidItem,"徽章未拥有 不可佩戴");
        //确保物品是徽章
        ItemConfig item = itemConfigDao.getById(firstValidItem.getItemId());
        AssertUtil.equal(item.getType(),ItemTypeEnum.BADGE.getType(),"佩戴的非徽章 请换别的");
        //更新用户表中的itemId
        userDao.wearingBadge(uid,itemId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void black(BlackReq blackReq) {
        Long uid = blackReq.getUid();
        Black black = Black.builder()
                .type(BlackTypeEnum.UID.getId())
                .target(uid.toString())
                .build();
        blackDao.save(black);
        User user = userDao.getById(uid);
        blackIp(Optional.ofNullable(user.getIpInfo()).map(IpInfo::getCreateIp).orElse(null));
        blackIp(Optional.ofNullable(user.getIpInfo()).map(IpInfo::getUpdateIp).orElse(null));
        //将拉黑的事件通知到前端 以及改变用户的状态 并且清空缓存
        applicationEventPublisher.publishEvent(new UserBlackEvent(this,user));
    }

    @Override
    public List<SummeryInfoDTO> getSummeryUserInfo(SummeryInfoReq req) {
        //获取redis中已经过期的用户
        List<Long> needSyncUidList = getNeedSyncUidList(req.getReqList());
        //加载用户信息
        Map<Long, SummeryInfoDTO> batch = userSummaryCache.getBatch(needSyncUidList);
        return req.getReqList()
                .stream()
                .map(a -> batch.containsKey(a.getUid()) ? batch.get(a.getUid()) : SummeryInfoDTO.skip(a.getUid()))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
    private List<Long> getNeedSyncUidList(List<SummeryInfoReq.infoReq> reqList) {
        List<Long> needSyncUidList = new ArrayList<>();
        //通过用户的key 读取redis中存的该用户的数据
        List<Long> userRedisData = userCache.getUserModifyTime(reqList.stream().map(SummeryInfoReq.infoReq::getUid).collect(Collectors.toList()));
        for (int i = 0; i < reqList.size(); i++) {
            SummeryInfoReq.infoReq infoReq = reqList.get(i);
            Long modifyTime  = userRedisData.get(i);
            //过期的数据返回
            if (Objects.isNull(infoReq.getLastModifyTime()) || (Objects.nonNull(modifyTime) && modifyTime > infoReq.getLastModifyTime())) {
                needSyncUidList.add(infoReq.getUid());
            }

        }
        return needSyncUidList;
    }

    @Override
    public List<ItemInfoDTO> getItemInfo(ItemInfoReq req) {
        return req.getReqList().stream().map(a -> {
            ItemConfig itemConfig = itemCache.getById(a.getItemId());
            if (Objects.nonNull(a.getLastModifyTime()) && a.getLastModifyTime() >= itemConfig.getUpdateTime().getTime()) {
                return ItemInfoDTO.skip(a.getItemId());
            }
            ItemInfoDTO dto = new ItemInfoDTO();
            dto.setItemId(itemConfig.getId());
            dto.setImg(itemConfig.getImg());
            dto.setDescribe(itemConfig.getDescribe());
            return dto;
        }).collect(Collectors.toList());
    }

    private void blackIp(String ip) {
        if(StringUtils.isBlank(ip)){
            return;
        }
        //拉黑ip
        try {
            Black black = Black.builder()
                    .type(BlackTypeEnum.IP.getId())
                    .target(ip)
                    .build();
            blackDao.save(black);
        }catch (Exception e){
            log.error(e.getMessage());
        }
    }
}
